package console.window;

import java.io.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.JTextArea;
import javax.swing.event.DocumentListener;
import javax.swing.event.DocumentEvent;
import javax.swing.SwingUtilities;

/**
 * This class is used to displays a Kawa scripter prompt in a windows.
 */
public class SwingMessageArea extends JTextArea implements KeyListener {
	public static final long serialVersionUID = 0x130;
	class SwingWriter extends java.io.Writer {
		javax.swing.JTextArea area;
		String str="";
		public SwingWriter (javax.swing.JTextArea area) {
			this.area = area;
		}
		public synchronized void write(int x) {
			str = str + (char) x;
			if (x == '\n') flush();
		}
		public void write (String str) {
			SwingMessageArea msg = (SwingMessageArea) area;
			msg.write(str);
		}
		public synchronized void write (char[] data, int off, int len) {
			flush();
			write(new String(data, off, len));
		}
		public synchronized void flush() {
			if (!str.equals("")) {
				write(str);
				str = "";
			}
		}
		public void close () {
			flush();
		}
	}
	SwingWriter out_stream;
	private PrintWriter out;
	private PrintWriter err;
	gnu.text.QueueReader in;

	int outputMark = 0;
	int endMark = -1;
	int length = 0;

	public SwingMessageArea(boolean focus, gnu.text.QueueReader in) {
		super();
		this.in = in;
		out_stream = new SwingWriter(this);
		SwingWriter err_stream = new SwingWriter(this);
		out = new PrintWriter(out_stream);
		err = new PrintWriter(err_stream);
		addKeyListener(this);
		final ConsoleDocument document = new ConsoleDocument();
		document.addDocumentListener(new FromDocument());
		setDocument(document);
		setLineWrap(true);
	}
	public synchronized int getOutputMark() {
		return(outputMark);
	}
	public synchronized int getEndMark() {
		return(endMark);
	}
	public synchronized void setOutputMark(int value) {
		outputMark = value;
	}
	public synchronized void setEndMark(int value) {
		endMark =value;
	}
	class ConsoleDocument extends javax.swing.text.PlainDocument {
		public static final long serialVersionUID = 0x1300;
		public synchronized void remove(int offs, int len) 
				throws javax.swing.text.BadLocationException {
			if(offs>=getOutputMark()) {
				super.remove(offs,len);
			}
			else {
				Toolkit.getDefaultToolkit().beep();
			}
		}
		public synchronized void insertString(int offs, String str, javax.swing.text.AttributeSet a)
				throws javax.swing.text.BadLocationException {
			if(offs>=getOutputMark()) {
				super.insertString(offs, str, a);
			}
			else {
				Toolkit.getDefaultToolkit().beep();
			}
		}
	}
	class FromDocument implements DocumentListener {
		public synchronized void insertUpdate(final DocumentEvent de) {
			int pos = getCaretPosition();
			String text = getText();
			int delta = text.length() - length;
			length += delta;
			if (pos < getOutputMark())
				setOutputMark(getOutputMark() + delta);
			else if (pos - delta < getOutputMark())
				setOutputMark(pos);
			if (getEndMark() >= 0) {
				if (pos < getEndMark())
					setEndMark(getEndMark()+delta);
				else if (pos - delta < getEndMark())
					setEndMark(pos);
			}
		}
		public synchronized void changedUpdate(DocumentEvent de) {
		}
		public void removeUpdate(DocumentEvent d) {
		}
	}
	synchronized void enter () {
		setCaretPosition(getOutputMark());
		int pos = getCaretPosition();
		String str = getText();
		int len = str.length();
		if (len != length) {
			System.err.println("(actual) len:"+len + " (saved) length:"+length);
		}
		int lineAfter = str.indexOf('\n', pos);
		setEndMark(-1);
		if (lineAfter < 0)
			lineAfter = len;
		int lineBefore = pos == 0 ? 0 : 1 + str.lastIndexOf('\n', pos-1);
		if (pos >= getOutputMark() || getOutputMark() <= lineAfter) {
			lineAfter = str.indexOf('\n', getOutputMark());
			if (lineAfter < 0) {
				lineAfter = len;
				append("\n");
			} else
				setEndMark(len);
			str = str.substring(getOutputMark(), lineAfter);
			str = str + '\n';
			setOutputMark(lineAfter+1);
		} else {
			str = str.substring(lineBefore, lineAfter+1);
			out_stream.write(str);
		}
		setCaretPosition(getOutputMark());
		if (in != null) {
			in.append(str);
			_updateHistory(str.replace('\n',' ').trim());
		}
	}

	// History
	private int _historyCursor = 0;
	private Vector<String> _historyCommands = new Vector<String>();
	public int historyLength = 20;

	// Replace the command with an entry from the history.
	void _nextCommand () {
		String text;
		if (_historyCursor == 0) {
			text = "";
		} else {
			_historyCursor --;
			text = (String)_historyCommands.elementAt(
					_historyCommands.size() - _historyCursor - 1);
		}
		replaceRangeJTextArea(
			text, getOutputMark(), getText().length());
	}

	// Replace the command with an entry from the history.
	void _previousCommand () {
		String text;
		if (_historyCursor == _historyCommands.size()) {
			return;
		} else {
			_historyCursor ++;
			text = (String)_historyCommands.elementAt(
				_historyCommands.size() - _historyCursor);
		}
		replaceRangeJTextArea(
			text, getOutputMark() , getText().length());
	}

	public void replaceRangeJTextArea(final String text,
							final int start, final int end) {
		Runnable doReplaceRangeJTextArea = new Runnable() {
			public void run() {
				replaceRange(text, start, end);
			}
		};
		SwingUtilities.invokeLater(doReplaceRangeJTextArea);
	}

	// Update the command history.
	public void _updateHistory (String command) {
		_historyCursor = 0;
		if (_historyCommands.size() == historyLength) {
			_historyCommands.removeElementAt(0);
		}
		_historyCommands.addElement(command);
	}

	public synchronized void keyPressed(KeyEvent e) {
		int code = e.getKeyCode();
		switch(code){
		case KeyEvent.VK_TAB:
			transferFocus();
			e.consume();
			break;
		case KeyEvent.VK_ENTER:
			enter();
			e.consume();
			break;
		case KeyEvent.VK_LEFT:
			if(getCaretPosition()<= getOutputMark()) {
				setCaretPosition(getOutputMark());
				e.consume();
			}
			break;
		case KeyEvent.VK_UP:
			setCaretPosition(getOutputMark());
			_previousCommand();
			e.consume();
			break;
		case KeyEvent.VK_DOWN:
			setCaretPosition(getOutputMark());
			_nextCommand();
			e.consume();
			break;
		case KeyEvent.VK_HOME:
			setCaretPosition(getOutputMark());
			e.consume();
			break;
		}
	}
	public void keyReleased(KeyEvent e) {
	}
	public void keyTyped(KeyEvent e) {
/*
		int code = e.getKeyCode();
		switch(code){
		case KeyEvent.VK_TAB:
			transferFocus();
			e.consume();
			break;
		}
*/
	}
	public synchronized void write (String str) {
		int len = str.length();
		if(len>0)
			insert(str, getOutputMark());
			setOutputMark(getOutputMark() + len);
		if (getEndMark() >= 0) {
			setEndMark(getEndMark() + len);
		}
		setCaretPosition(getOutputMark());
	}
	public synchronized void deleteOldText () {
		String str = getText();
		int lineBefore = (getOutputMark() <= 0 ? 0
				: (str.lastIndexOf('\n', getOutputMark()-1)) + 1);
		setCaretPosition(getOutputMark());
		replaceRange("", 0, lineBefore);
	}
	public PrintWriter getStdout() {
		return out;
	}
	public PrintWriter getStderr() {
		return err;
	}
	public void clear() {
		outputMark = 0;
		endMark = -1;
		length = 0;
		setText("");
	}
}
